import { _decorator, Component, Node, CCInteger, Prefab, instantiate, v3, CCBoolean, director, Vec3, Vec2, v2 } from 'cc';
import { uWait } from '../../ngame/util/util-func';
import GameConst from '../GameConst';
// import { uCCWait } from '../../ngame/util/util-func';
import { IObject } from '../scene/IObject';
import { AStarEnum, AStarGridTool, AStarInfo } from './tools/AStarGridTool';
const { ccclass, property,executeInEditMode } = _decorator;


/**
 * A* 寻路管理器
 */
@ccclass('AStarManage')
// @executeInEditMode(true)
export class AStarManage extends Component {

    @property({ type:CCInteger, displayName:"宽度" })
    width:number = 0;
    
    @property({ type:CCInteger, displayName:"高度" })
    height:number = 0;

    @property({ type:Prefab, displayName:"阻止网格" })
    preventPath:Prefab = null;

    @property({ type:Prefab, displayName:"寻路网格" })
    astarPath:Prefab = null;

    //格子列表
    grids:AStarGridTool[][] = [];

    _apply:boolean = false;
    //动态生效
    @property({ type:CCBoolean, displayName:"动态生效"})
    set apply(value){ this.init();this._apply = value;}
    get apply(){ return this._apply;}

    @property({ type:CCBoolean, displayName:"是否debug" })
    isDebug:boolean = false;

    //是否加载完成
    isSuccess:boolean = false;

    w:number = 0;
    h:number = 0;


    // async onLoad(){

    //     // if(this.grids.length === 0)
    //     this.scheduleOnce(async () => {
    //         await this.init();
    //     })

    // }

    async init() {

        if(GameConst.IsEditor && !(this.isDebug)) {
            this.initDestroy();
            return;
        };

        this.initDestroy();

        // let x = this.node.scale.x;
        // let y = this.node.scale.z;
        console.log("生成寻路网格中...");

        let w = 10/this.width;
        let h = 10/this.height;

        this.w = w;
        this.h = h;

        let block = new Node();
        block.parent = this.node;

        //生成网格
        for (let width = 0; width < this.width; width++) {
            
            if(!this.grids) this.grids = [];
            if(!this.grids[width]) this.grids[width] = [];

            for (let height = 0; height < this.height; height++) {

                // let grid = instantiate(this.default);
                // grid.name = `Block_${width}_${height}`;
                // (<any>grid)._uuid = `grid.uuid_${width}_${height}`
                // grid.position = v3(((w*width)+(w/2) - 5),0.01,((h*height)+(h/2) - 5));
                // let gridTool = grid.addComponent(AStarGridTool);
                // gridTool.x = width;
                // gridTool.y = height;
                // grid.scale = v3(1/this.width,grid.scale.y,1/this.height);
                // grid.parent = this.node;

                block.position = v3(((w*width)+(w/2) - 5),0,((h*height)+(h/2) - 5));

                this.grids[width][height] = Object.assign(new AStarGridTool(),{
                    x:width,
                    y:height,
                    pos: block.position.clone(),
                    worldPos: block.worldPosition.clone()
                });
                
                
            }           
        }

        block.destroy();
        
        this.noMoveJSON();

        console.log("生成完成...",this.grids);
        this.isSuccess = true;

    }

    //生成禁止走的JSON
    noMoveJSON(){

        //找到场景中所有建筑
        let objects = director.getScene().getComponentsInChildren(IObject);
        
        let start = this.grids[0][0].worldPos.clone();
        let end = this.grids[this.width-1][this.height-1].worldPos.clone();
        
        let wh = end.clone().subtract(start);
        console.log(wh);

        // let wh = end.clone().multiplyScalar(2).divide(v3(this.width,0,this.height));
        // console.log(end.clone().multiplyScalar(2),wh);

        objects.forEach((item) => {

            //找到接近物体的区域

            let worldPos = item.node.worldPosition.clone();
            // console.log(start,end,worldPos);
            
            //判断是否超过边界
            if(!((worldPos.x > start.x && worldPos.z > start.z) && (worldPos.x < end.x && worldPos.z < end.z))) {
                console.log(item,"越界了");
                return;
            };

            //推算阻止区域
            // console.log("Vec3.rotateY",item.size.clone());

            // item.size = Vec3.rotateY(item.size,Vec3.ZERO,Vec3.ZERO,90);

            // console.log("Vec3.rotateY",item.size);

            // item.size = new Vec3(0.5,0,1);

            //如果没有占地大小则不进行阻止
            if(item.size.equals(Vec3.ZERO)) return;

            let worldPosClone = worldPos.clone();
            worldPosClone.add(end);

            //标识中心点
            let x = Math.floor(this.width * (worldPosClone.x / wh.x));
            let z = Math.floor(this.height * (worldPosClone.z / wh.z));
            this.addPrevent(x,z);

            //通过大小推算覆盖点
            // let coverLPos = worldPosClone.subtract(item.size.clone());
            let coverRPos = worldPos.clone().add(v3(item.size.x,0,-item.size.z));
            let coverRPosX = worldPos.clone().add(v3(-item.size.x,0,item.size.z));

            console.log(coverRPos,coverRPosX);

            let subx = 0;
            let subz = 0;

            while(z + subz >= 0 && z + subz < this.width && this.grids[x + subx][z + subz].worldPos.z > coverRPos.z){
                
                this.addPrevent(x + subx,z + subz);
                
                while(x + subx >= 0 && x + subx < this.width && this.grids[x + subx][z + subz].worldPos.x < coverRPos.x){

                    this.addPrevent(x + subx,z + subz);
                    subx+=1;

                }

                subx = 0;
                
                while(x - subx >= 0 && x - subx < this.width && this.grids[x - subx][z + subz].worldPos.x > coverRPosX.x){

                    this.addPrevent(x - subx,z + subz);
                    subx+=1;

                }

                subz-=1;
                subx = 0;

            }
            
            subx = 0;
            subz = 0;


            if(!item.isMirror) return;
            
            while(z + subz >= 0 && z + subz < this.width && this.grids[x + subx][z + subz].worldPos.z < coverRPosX.z){
                
                this.addPrevent(x + subx,z + subz);
                
                while(x + subx >= 0 && x + subx < this.width && this.grids[x + subx][z + subz].worldPos.x < coverRPos.x){

                    this.addPrevent(x + subx,z + subz);
                    subx+=1;

                }

                subx = 0;
                
                while(x - subx >= 0 && x - subx < this.width && this.grids[x - subx][z + subz].worldPos.x > coverRPosX.x){

                    this.addPrevent(x - subx,z + subz);
                    subx+=1;

                }

                subz+=1;
                subx = 0;

            }
            
            
        });


    }

    worldToXY(worldPos:Vec3){

        if(!this.isSuccess) return v3();

        let start = this.grids[0][0].worldPos.clone();
        let end = this.grids[this.width-1][this.height-1].worldPos.clone();
        let wh = end.clone().subtract(start);
        
        let worldPosClone = worldPos.clone();
        worldPosClone.add(end);

        return {x : Math.floor(this.width * (worldPosClone.x / wh.x)),y:Math.floor(this.height * (worldPosClone.z / wh.z))}
        
    }

    addPrevent(x,z){
        if(this.grids[x][z].isPrevent || (x < 0 || z < 0)) return;
        this.grids[x][z].isPrevent = true;
        if(!this.isDebug) return;
        let grid = this.grids[x][z].node = instantiate(this.preventPath);
        grid.parent = this.node;
        grid.scale = v3(1/this.width,grid.scale.y,1/this.height);
        grid.position = this.grids[x][z].pos.clone().add(v3(0,0.01,0));
    }

    initDestroy(){

        this.node.destroyAllChildren();
        
    }

    worldToV2(pos:Vec3){
        if(!this.isSuccess) return v2();
        return Object.assign(v2(),this.worldToXY(pos));
    }

    ToAStar3World(start:Vec3,end:Vec3){

        if(!this.isSuccess) return;
        return this.ToAStar(this.worldToV2(start),this.worldToV2(end));

    }

    //A*寻路算法
    ToAStar(start:Vec2,end:Vec2){

        console.log("ToAStar - 开始寻路",start,end);
        

        //基本过滤
        if(this.grids[start.x][start.y].isPrevent || this.grids[end.x][end.y].isPrevent) {
            console.log("寻路错误...前往地点是禁止区");
            return;
        }

        //检测路线
        let openList:AStarInfo[] = [AStarInfo.toAStarInfo(this.grids[start.x][start.y],AStarEnum.START)];

        //当前格子
        let current:AStarInfo = openList[0];

        //完成检测的点
        let closeList:boolean[][] = [];
        // let closeList:string[] = [];

        //使用的点
        let useList:AStarInfo[][] = [];

        let that = this;

        function addUseList(newAStarInfo){
            if(!useList[newAStarInfo.x]) useList[newAStarInfo.x] = [];
            if(useList[newAStarInfo.x][newAStarInfo.y]) return;
            useList[newAStarInfo.x][newAStarInfo.y] = newAStarInfo;
        }
        function getUseList(x,y) : AStarInfo{
            if(!useList[x]) useList[x] = [];
            let newAStarInfo = useList[x][y];

            if(newAStarInfo) return newAStarInfo;

            if(x != end.x || y != end.y){
                newAStarInfo = AStarInfo.toAStarInfo(that.grids[x][y],AStarEnum.MOVE)
            }else{
                newAStarInfo = AStarInfo.toAStarInfo(that.grids[x][y],AStarEnum.END)
            }

            useList[x][y] = newAStarInfo;
            return useList[x][y];

        }

        function isCloseList(x,y){
            if(!closeList[x]){
                closeList[x] = []
            }
            return closeList[x][y];
        }

        function addCloseList(x,y){
            if(!closeList[x]){
                closeList[x] = []
            }
            closeList[x][y] = true;
        }
        
        addUseList(current);

        let maxNum = 1000;
        let addNum = 0;

        //循环寻路 (A*) 循环找到终点
        while(openList.length > 0 && current.type != AStarEnum.END
            // && addNum < maxNum
            ){

            addNum++;

            //获取此时最小F点
            current = openList[0];

            //如果当前点就是目标
            if (current.type == AStarEnum.END) {
                return this.ToGenerateResult(current);
            }
            
            //上下左右，左上左下，右上右下，遍历
            for (let i = -1; i <= 1; i++) {
                for (let j = -1; j <= 1; j++) {

                    if (i != 0 || j != 0) {
                        //计算坐标
                        let x = current.x + i;
                        let y = current.y + j;


                        //如果未超出所有格子范围，不是障碍物，不是重复点
                        if (x >= 0 && y >= 0 && x < this.width && y < this.height
                            && !(this.grids[x][y].isPrevent)
                            && !(isCloseList(x,y))
                            // && closeList.indexOf(`${x}-${y}`) === -1
                            ) {

                                let newAStarInfo = getUseList(x,y);

                                //计算G值
                                let g = current.g + Math.sqrt(Math.abs(i) + Math.abs(j));

                                //与原G值对照 取最小的G
                                if (newAStarInfo.g == 0 || newAStarInfo.g > g){
                                    newAStarInfo.g = g;
                                    newAStarInfo.parent = current;
                                }

                                //计算H值 (曼哈顿方式计算H值)
                                newAStarInfo.h = Math.abs(end.x - x) + Math.abs(end.y - y);
                                
                                //计算F值
                                newAStarInfo.f = newAStarInfo.g + newAStarInfo.h;

                                if(openList.indexOf(newAStarInfo) == -1)
                                    openList.push(newAStarInfo);
                                
                                openList = openList.sort((a,b) => a.f - b.f);

                        }

                    }

                }
            }

            addCloseList(current.x,current.y);
            // closeList.push(`${current.x}-${current.y}`)
            openList.splice(openList.indexOf(current),1);
            if(!openList.length){
                console.log("未找到路径");
            }

        }

        console.log("迷路了!");
        return;

    }

    ToGenerateResult(current:AStarInfo,list:AStarInfo[] = []){
        if(current.parent){
            list.push(current = current.parent);
            this.ToGenerateResult(current,list);
        }
        return list;
    }

    debugPath(list:AStarInfo[] | AStarGridTool[]){
        list && this.debugPathTool(list.map(info => info.point));
    }

    debugPathTool(list:AStarGridTool[]){
        
        list.forEach(info => {

            let astar = info;
            if(astar.node) astar.node.destroy();
            if(!this.isDebug) return;
            let grid = astar.node = instantiate(this.astarPath);
            grid.parent = this.node;
            grid.scale = v3(1/this.width,grid.scale.y,1/this.height);
            grid.position = astar.pos.clone().add(v3(0,0.01,0));

        })

    }

    update(deltaTime: number) {
        
    }

}

